/**
 * \file SharedPointer.h
 * \brief SharedPointer definition and declaration class
 * \author Edouard ROGE
 * \date 28 august 2012
 */

#ifndef _SHAREDPOINTER_
#define _SHAREDPOINTER_

#include <stdlib.h>
#include "Core/Define/Define.h"
#include "Core/Thread/Mutex.h"
#include "Core/Memory/MemoryPoolManager.h"

/**
 * \class SharedPointer
 * \brief class with template to manage pointer
 */
template < class T >
class SharedPointer
{
public:
	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	SharedPointer();

	/**
	 * \fn SharedPointer(const SharedPointer<T> & _pSmartPointer);
	 * \param _pSmartPointer sharedpointer to copy
	 * \brief Copy constructor
	 */
	SharedPointer(const SharedPointer<T> & _pSmartPointer);

	/**
	 * \fn SharedPointer(T * _pPointer);
	 * \param _pPointer pointer to shared
	 * \brief Constructor with parameters
	 */
	SharedPointer(T * _pPointer);

	/**
	 * \fn ~SharedPointer();
	 * \brief Destructor
	 */
	~SharedPointer();

	/**
	 * \fn T * Get() const;
	 * \return the shared object
	 * \brief get the shared object
	 */
	T * Get() const;

	/**
	 * \fn SharedPointer<T> & operator=(const SharedPointer<T>& _pSmartPointer);
	 * \param _pSmartPointer new value for the shared pointer
	 * \return (*this)
	 * \brief operator =
	 */
	SharedPointer<T> & operator=(const SharedPointer<T>& _pSmartPointer);

	/**
	 * \fn SharedPointer<T> & operator=(T * _ptr);
	 * \param _ptr new pointer
	 * \return (*this)
	 * \brief operator =
	 */
	SharedPointer<T> & operator=(T * _ptr);

	/**
	 * \fn T* operator->() const;
	 * \return the shared object
	 * \brief operator ->
	 */
	T* operator->() const;

	/**
	 * \fn T& operator*() const;
	 * \return the shared object by reference
	 * \brief operator *
	 */
	T& operator*() const;

	/**
	 * \fn void Reset();
	 * \brief Set to null the shared object
	 */
	void Reset();

	/**
	 * \fn void Reset(T * _pObject);
	 * \param _pObject new shared object
	 * \brief Rest the shared pointer with a new pointer
	 */
	void Reset(T * _pObject);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator == (const SharedPointer<T1> & _o1, const SharedPointer<T1> & _o2);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator == (const SharedPointer<T1> & _o1, T1 * _o2);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator == (T1 * _o1, const SharedPointer<T1> & _o2);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator != (const SharedPointer<T1> & _o1, const SharedPointer<T1> & _o2);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator == (const SharedPointer<T1> & _o1, T1 * _o2);

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	//template < class T1 > friend bool operator == (T1 * _o1, const SharedPointer<T1> & _o2);


private:
	/** Object to share */
	T* m_pObject;

	/** */
	uint32 * m_iRef;

	/** Mutex */
	Mutex m_Mutex;

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	void AddRef();

	/**
	 * \fn SharedPointer();
	 * \brief Default constructor
	 */
	void Release();
};


template < class T >
void SharedPointer<T>::AddRef()
{
	m_Mutex.Lock();
	++(*m_iRef);
	m_Mutex.Unlock();
}

template < class T >
void SharedPointer<T>::Release()
{
	m_Mutex.Lock();
	if(m_iRef && !--(*m_iRef))
	{
		MemoryPoolManager::GetInstance().Free(m_iRef);
		MemoryPoolManager::GetInstance().Free(m_pObject);
		m_pObject=NULL;
	}
	m_Mutex.Unlock();
}

template < class T >
SharedPointer<T>::SharedPointer()
: m_pObject(NULL), m_iRef(NULL)
{

}

template < class T >
SharedPointer<T>::SharedPointer(const SharedPointer<T> & _pSmartPointer)
{
	m_iRef=_pSmartPointer.m_iRef;
	m_pObject=_pSmartPointer.m_pObject;
	AddRef();
}


template < class T >
SharedPointer<T>::SharedPointer(T * _pPointer)
{
	m_pObject=_pPointer;
	m_iRef=(uint32*)MemoryPoolManager::GetInstance().Allocate(sizeof(uint32));
	*m_iRef=0;
	AddRef();
}

template < class T >
SharedPointer<T>::~SharedPointer()
{
	Release();
}

template < class T >
T * SharedPointer<T>::Get() const
{
	return m_pObject;
}

template < class T >
SharedPointer<T> & SharedPointer<T>::operator=(const SharedPointer<T>& _pSmartPointer)
{
	Release();
	m_iRef=_pSmartPointer.m_iRef;
	m_pObject=_pSmartPointer.m_pObject;
	AddRef();

	return (*this);
}

template < class T >
SharedPointer<T> & SharedPointer<T>::operator=(T * _ptr)
{
	Release();
	m_pObject=_ptr;
	m_iRef=(uint32*)MemoryPoolManager::GetInstance().Allocate(sizeof(uint32));
	*m_iRef=0;
	AddRef();

	return (*this);
}

template < class T >
T* SharedPointer<T>::operator->() const
{
	return m_pObject;
}

template < class T >
T& SharedPointer<T>::operator*() const
{
	return *m_pObject;
}

#endif // _SHAREDPOINTER_
